<?
/*** class Core  - библиотека основных функций ядра  ***/

Core::init();

function Config($module)
{
	$module = strtolower($module);
	// pr(Core::$Modules);
	// pr($module);
	// pr(Core::$Modules[$module]);
	
	if (isset(Core::$Modules[$module]))
	{
		$result =  Core::$Modules[$module];
		return $result;
	}
	else return null;	
}
    
class Core 
{
    
    // объект со свойствами текущего запроса  
	static public $Request ;
	
	// объект со свойствами текущей страницы 
	static public $Page ;
	
	
	// объект со свойствами текущего пользователя 
	// static public $User ;
			
	// массив с конфигами модулей
	static public $Modules = array();
	
	// зарегистрированные модулями urlword для первой секции URL-пути  
	static public $Urlwords = array();
	
	// список виджетов, генерируемых модулями
	static public $Widgets = array();
	
	// зарегистрированные хуки (обработчики событий)
	static $Hooks = array();
	
	
	/* 	Core::$view_methods  - список зарегистрированных модулями функций-генераторов HTML-кода. Формат массива: 
		array('view_id' => 'ModuleClassname::funcname', ... )
	*/	
	static $view_methods = array();
	
	
	
	
	
	
	
    
/**  Core::parse_params($params) - интерпретирует стандартные параметры, которые передаеются в функции-генераторы данных для получения списков объектов. 

Возвращает необходимые составляющие для SQL запроса, с учетом взаимозависимых параметров, а также с учетом переданых в URL параметров, влиящих на выборку номера страницы / поиска / сортировки. Может также возвращать готовую выборку из базы данных, если применение доп. условий и модификаций SQL-запроса не требуется.

	@param string $params  
		Принимает строку с параметрами в формете URL (var1=value1&var2=value2). 
	@param string $table
		Имя таблицы в базе данных, из которой будет производиться выборка. необходимо для подсчета общего количества страниц. (анпримр: #blog_posts)
	
	Допустимые параметры и их значения:
	* limit 	- максимальное количество возвращаемых элементов (если не указано, берется из limit_default  или 10)
	* limit_default - используется если не указан limit 
	* start	- начальная позиция выборки, по умолчанию 0
	* pg 		- страница выборки, более приоритетно чем start, по умолчанию 1
	* order	- поле/поля для сортировки, раделенные запятыми, например: 'order=price,desc,title'. Допустимые значения в списке: имена сущесвтующих полей, а также asc и desc. Сортировка в блоге по умлчанию: 'post_date,DESC'  
	* order_default - используется, если не указан order
	
	* parse_url - если установлено (parse_url=1), то будет также производиться парсинг аналогичных параметров из GET параметров в URL, которые будут более приоритетны, чем заданные в аргументе функции 
	
	* exec_sql - если установлено (exec_sql=1), то полученный SQL выполняется для получения списка объектов из базы, а также общего количества объектов (для постраничной навигации). Результат выборки возвращается в свойстве 'data'
	
	@return array
		Возвращает ассоциативный массив с готовыми к применению составляющими  SQL-запроса для выборки списка объектов: 
		
	return (object)array(
		'func_params'=> $func_params, // параметры, переданные в функцию
		'url_params' =>$_GET, //  параметры, переданные через URL 
		
		'limit'	=> $limit, // параметры выборки из базы для подстановки в SQL
		'order'	=> $order,
		'where'	=> $where,
		
		'sql'	=> $sql,   //"SELECT * FROM $table WHERE %where% ORDER BY %order%  LIMIT %limit%"
		'sql_total'	=> $sql_total,  "SELECT count(id) FROM $table WHERE %where%";
		
		'pagination'=> $pagination, // ссылка на объект Core::$Page->pagination со свойствами для построения постраничной навигации
		
		'data'		=> $data, // если был установлен $params['exec_sql'], то здесь содержится двухмерный массив - результат выборки из базы данных
	);
	
	

**/
	
	
	
	
	// инициализация ядра
	static function init()
	{
		//self::set_return_url();
		
		self::$Hooks['all']=array();
		
	}
	
	
	static public
    function parse_params(&$params, $table, $field_prefix='')
	{
		$sql_params = (object)null;
		
		if (!is_array($params)) parse_str($params, $params);
		$sql_params->func_params = $params;
		$sql_params->url_params	 = $_GET;
		
		$parse_url = val($params['parse_url']) ? true : false;
		
		//pr ($params);
		
        // формируем $sql_limit для SQL запроса 
		$limit = $parse_url ? intval(GET('limit')) : 0 ;
		if (!$limit) $limit = intval(val($params['limit']));
		if (!$limit) $limit = intval(val($params['limit_default']));
		if (!$limit) $limit = 10;
        //pr($limit);
		
		$pg = $parse_url ? intval(val($_GET['pg'])) : 0 ;
		if (!$pg) $pg = intval(val($params['pg']));
		
		$start = $parse_url ? intval(val($_GET['start'])) : 0 ;
		if (!$start) $start = intval(val($params['start']));
		$start = $pg ?  $limit*($pg-1) : $start;
			
        $sql_params->limit =  $start.','.$limit;
        
		//$params['exec_sql']=1;
		
        // формируем $sql_order для запроса 
		$sql_params->order = '';
		$order = $parse_url ? val($_GET['order']) : '' ;
		if (!$order) $order = val($params['order']);
		if (!$order) $order = val($params['order_default']);
		
		// проверяем на допустимость каждое имя поля в order 
		// pr($order);
		if ($order)
		{
			$order_items = explode(',', $order);
			//pr($order_items);
			// вставляем в $sql_params->order только допустимые имена полей
			foreach ($order_items as $field)
			{
				$field = strtolower(trim($field));
				
				if	( 
					  $db_field = Db::field_exists($table, $field, $field_prefix)
					)
					$sql_params->order .= ", $db_field";
				else 
				if (in_array ($field, array('asc','desc')))
					$sql_params->order .= " $field ";
			}
			$sql_params->order = trim($sql_params->order, ',');
		}	
		//pr($sql_params);
		
		
		/* формируем $where для запроса */
		$sql_params->where=' 1 ';
		
		// админу показываем все элементы, включая скрытые
		
		$is_admin = User::$User->is_admin;
		if ($is_admin) $is_admin = val($params['is_admin']);
		if (!$is_admin) $sql_params->where .= ' AND show=1 ';
		
		
		// проверяем - есть ли условие на _равенство_ с каким-то полем из базы данных?
		$check_fields = empty($parse_url) ? 
							$params : array_merge($params, $_GET);
		foreach ($check_fields as $field=>$value)
			if ($field_db = Db::field_exists($table, $field, $field_prefix))
				{
					if (!$value) continue;
					if ($value=='null')
						$sql_params->where.= " AND ($field_db is null 
							 OR $field_db='' OR $field_db=0 ) ";	
					else	
						$sql_params->where.=
							 " AND ($field_db = ".Db::quote($value).") ";	
				}
		 //pr($sql_params);

		// pr(Db::get_results("SELECT * FROM #blog_posts WHERE  1  AND (page_id is null OR id=26)   ORDER BY  post_date  LIMIT 0,5"));
		
		// проверяем - есть ли условие на _фильтрацию_ по какому-то полю?
		foreach ($check_fields as $field=>$value)
		{
			if ( $value && 0===strpos($field , 'filter_') && ($field=substr($field, 7))
				 && ($field_db = Db::field_exists($table, $field, $field_prefix))
				) 
				{
					//pr($field);
					// эмулируем "регистронезависмый" поиск для русского языка в SQLite
					$value1 = $value;
					$value2 = mb_strtolower($value);
					$value3 = mb_strtoupper($value);
					$value4 = mb_substr($value3,0,1) . mb_substr($value2,1);
					list($value1,$value2,$value3,$value4) = array(Db::quote("%$value1%"), Db::quote("%$value2%"), Db::quote("%$value3%"), Db::quote("%$value4%"));
					
					$sql_params->where.=" 
						AND (
							   $field_db LIKE {$value1} 
							OR $field_db LIKE {$value2} 
							OR $field_db LIKE {$value3} 
							OR $field_db LIKE {$value4} 
						) 
					";	
				}
		}
		// pr($check_fields);
		 // pr($sql_params);
		
		
		
		// устанавливаем (в объекте страницы) свойства для постраничной навигации 
		Core::$Page->pagination =
		(object)array(
					'per_page' 	=> $limit,
					'start'  	=> $start,
					'pg'		=> ceil(($start + $limit) / $limit ),
					'total' 	=> null,
				);	
		$sql_params->pagination =& Core::$Page->pagination;
		
		
		
		// вычисляем SQL выражения для получения списка объектов и количества объектов
		$sql_params->sql = "SELECT * FROM $table WHERE %where% ORDER BY %order%  LIMIT %limit%";
		$sql_params->sql_total = "SELECT count(id) FROM $table WHERE %where%";
		
		//pr($sql_params);
		if (val($params['exec_sql']))
			Core::exec_sql($sql_params);
		//pr($sql_params);
		
		return $sql_params;
		
	}
	
    
	// интерпретирует и выполняет объект со своствами sql запроса, подготовленый в функции Core::parse_params()
    static public
    function exec_sql(&$sql)
	{
		// вычисляем список объектов
		$sql->sql = str_replace(array('%where%','%order%','%limit%'),
								array($sql->where,$sql->order,$sql->limit), 
								$sql->sql
							   );
		$sql->data = Db::get_results($sql->sql);
		
		// вычисляем общее количество объектов в базе
		$sql->sql_total = str_replace('%where%',$sql->where,$sql->sql_total);
		$sql->pagination->total = Db::get_var($sql->sql_total);
		
		return $result =& $sql->data;
	}
	
	
    // пока не применяется
    static public
    function get_url($module,$action,$params)
    {
        if (is_array($params)) $params = implode('/', $params);
        
        
        if ($module=='site' && $action=='page')
            $url = "/".$params;
        else
            $url = "{$module}/{$action}/" . $params;
        
        // :TODO: нужно в будущем учесть, что URL может быть разный в зависимости от установки админ-режима и от реализованной системы адресации
        
            
        return $url;    
    }
    
	
	
    // ищет в списке папок все файлы вида 'hook.<hook_id>.php' и добавляет их в массив "хуков"   
	static public
	function find_hooks($dirlist)
	{
		if (!is_array($dirlist)) $dirlist = array($dirlist);
		// просматриваем все директории, ищем хуки
		foreach ($dirlist AS $dir) 
		{
			if (!is_dir($dir)) continue;
			// получаем список файлов похожихна хуки 
			$hooklist = file_list($dir, $filter='hook.');
			
			// ищем правильные файлы-хуки и записываем их в массив хуков
			foreach ($hooklist AS $hookfile) 
			{
				if ('hook.'== substr($hookfile,0,5) 
					&& 'php' == file_ext($hookfile) )
				{
					$hook_id = substr($hookfile, 5, -4);
					$hookpath = $dir ."/". $hookfile;
					
					// не допускаем, чтобы один хук добавлялся дважды: 
					if (in_array ($hookpath, self::$Hooks['all'])) continue;
					
					// добавляем хук в коллекцию файлов-хуков
					self::$Hooks['all'][] = $hookpath;
					self::$Hooks[$hook_id][] = $hookpath;
					
				}	
			}
		
		}	
	}

	
	// добавление хука - PHP кода выполняемого по событию
	/* static
	function add_hook($hook_id, $hook_code)
	{
		// инициализируем переменную хуков
		if (!isset(self::$Hooks[$hook_id]))  
			self::$Hooks[$hook_id]='';
		
		// защищаемся от повторного добавления кода хука
		if (strstr(self::$Hooks[$hook_id], $hook_code)) 
			return;
		
		// добавляем код хука в конец кода
		self::$Hooks[$hook_id] .= $hook_code;
		
	} */
	
	// удаление обоработчиков события
	// static function del_hook($hook_id, $delete_hook='')
	// {
		// return ( self::$Hooks[$hook_id]= str_replace($delete_hook,'', @self::$Hooks[$hook_id]));
	// }
	
	// вызов обработчика события
    static function run_hook($hook_id, $params = array()) 
    {
		// pr($hook_id);
		// echo "$hook_id,";
		// pr(self::$Hooks);
		
		if(empty(self::$Hooks[$hook_id])) return;
		
		foreach ( (array)self::$Hooks[$hook_id] AS $hookfile )
			if (is_file($hookfile))
				include ($hookfile);
		
		
		//eval(self::$Hooks[$hook_id]);
	}
	
   
    
    
   
    
    
    

}




